home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Scene 96
/
Scene 96 International Edition (Zyklop Software) (Disc 2) (1997).iso
/
misc
/
coding
/
vgacodng
/
part05.txt
< prev
next >
Wrap
Text File
|
1996-11-22
|
19KB
|
369 lines
VGA-Kurs - Part #5
Herzlich Willkommen zu 'T.C.P.'s Beginners Guide To VGA Coding', Part V.
In diesem Teil werden wir den in der Demo-Scene am meisten genutzten
Videomodus besprechen, den Mode-X.
Der Mode-X ist, wie viele sicherlich vermuten, kein Standardmodus der VGA.
Trotzdem ist er eigentlich ein sehr gewöhnlicher Videomodus, der wie die
Hires-Modi aufgebaut ist, d.h. er wird nicht direkt, wie beim Modus 13h,
sondern über Planes adressiert.
Eigentlich ist der Modus 13h ja auch eine Abart des Mode-X, und nicht
umgekehrt. Die Entwickler der VGA-Karte wollten den Modus 13h möglichst einfach
programmierbar machen. Bei den Vorgängern der VGA wurde der Bildschirmspeicher
immer in Verbindung mit Planes angesprochen. Diese sind im Modus 13h durch
Abschalten des sog. Chain-4-Modus nicht aktiv, und man kann den
Bildschirmspeicher direkt ansprechen.
Eine Plane ist sozusagen ein Bildschirm. Im EGA-Modus z.B. muß man, um ein Pixel
zu setzen, erst die Plane bestimmen, auf die der Punkt gesetzt werden soll, um
anschließend den Punkt an die gewünschten Koordinaten zu setzen. Dies erscheint
komplizierter als beim Modus 13h, jedoch stößt man, wenn man nur in diesem
Modus 13h programmiert, sehr bald auf Grenzen. Ein Beispiel:
Der PC wurde im Laufe der Jahre immer weiter entwickelt, genau wie die
VGA-Karten. Schneller, mehr Speicher, höhere Auflösungen. Doch trotz aller
Rechenpower gibt es eine Einschränkung: Bei manchen, sehr rechenaufwendigen
VGA-Operationen, schafft es die Hardware einfach nicht, den Bildschirm
innerhalb eines Strahlrücklaufes (Retrace) vollständig darzustellen.
Bewegt sich nun zufälligerweise genau in dem Zeitpunkt, in dem das Bild
aufgebaut wird, der Kathodenstrahl über den Schirm, hat das fatale Folgen.
In der oberen Hälfte des Bildschirms, die schon neu aufgebaut wurde, ist das
nächste Frame zu sehen, in der unteren Hälfte noch das alte. Man muß also vor
der Bildschirmoperation abwarten, bis der Retrace vorüber ist, und dann das
Bild aufbauen. Was aber, wenn während der kurzen Zeit, in der kein Neuaufbau
des Bildes erfolgt, nicht genug Zeit ist, um z.B. ein komplexes Vektorgebilde
darzustellen?
Jetzt muß eine zweite Bildschirmseite her, auf der man das Bild berechnen, und
dann -schwupp- nach dem Bildschirmaufbau auf den Screen kopieren kann.
Woher aber bekommt man zusätzliche Seiten (Pages)? In der letzen Ausgabe haben
wir uns mit dem Virtual Screen beschäftigt, einer simulierten zweiten Page,
die in den Arbeitsspeicher eingeblendet wird.
Diese Methode erfüllt zwar ihren Zweck, jedoch hat sie einige gravierende
Nachteile:
1. Ein solcher Virtual Screen beansprucht 64000 Byte Arbeitsspeicher. Das ist
an sich nicht so viel, in einigen Sonderfällen benötigt man allerdings
mehr als einen VS, und dann kann's schon mal eng werden.
2. Um den Inhalt des VS auf den Screen zu kopieren, müssen 64000 Byte
verschoben werden, was einige Rechenzeit beansprucht, und auf langsameren
Rechnern zu Problemen führen kann.
Diese Nachteile haben wir beim Mode-X nicht, hier werden ganze 4 Pages in die
64 KB des Bildschirmspeichers gepackt, d.h. es wird kein zusätzlicher
Arbeitsspeicher benötigt und, das wichtigste, eine Page ist nur 16000 Byte
groß, es wird also nur ein Viertel der Rechenzeit benötigt, die beim VS nötig
wäre.
Aber wie in aller Welt soll das funktionieren?
Ganz einfach, wie zu Beginn gesagt, muß beim Mode-X erst die Plane bestimmt
werden, auf die geschrieben/von der gelesen werden soll. Also steht ein Byte
im Bildschirmspeicher für vier Pixel auf dem Screen. Die ersten 16000 Byte
stehen demnach für die 64000 Pixel der ersten Page, die Bytes 16000 bis
31999 für die der zweiten Page, usw.
Ach so, paßt auf, daß ihr die Begriffe 'Page' und 'Plane' nicht durcheinander
bringt, das sind zwei grundverschiedene Dinge.
Man kann sich die Planes wie vier übereinandergelegte Ebenen vorstellen. Sucht
man sich einen Punkt auf der ersten Ebene, hat man noch 3 auf den anderen
Ebenen, die die gleichen Koordinaten haben. Will man den Punkt also genau
definieren, muß auch die Plane dazu angegeben werden.
Auf der ersten Plane liegen alle Punkte, die durch 4 dividiert den Rest 0
ergeben, auf der zweiten die, die den Rest 1 ergeben, usw.
Ein Beispiel: Will man das dritte Pixel der ersten Page lesen, selektiert man
zuerst Plane 3 (3 div 4 = 0, Rest 3), und liest nun das erste Byte des
Bildschirmspeichers.
Soll stattdessen das 23456. Pixel der dritten Page beschrieben werden, wird es
etwas komplizierter, hier benötigt man konkrete Formeln zur Bestimmung des
Offsets und der Plane, hier kommen sie:
Offset = (Y-Koord * 320 + X-Koord) div 4
Dies können wir umformen zu:
Offset = Y-Koord * 80 + X-Koord div 4,
wobei wir das 'div 4' durch ein schnelleres 'shr 2' ersetzen können.
Die Formel zur Bestimmung der Plane:
Plane = X-Koord mod 4,
also der Rest der Division X-Koord div 4.
Im Modus 13h wird diese Berechnung automatisch vorgenommen, um den einfachen
Zugriff zu ermöglichen.
Doch nun zuerst zum Wichtigsten, dem Setzen des Mode-X. Dazu müssen zuerst der
Chain-4-Mechanismus und der Odd/Even-Mode, d.h. die Plane Selektion per
unterstem Offset-Bit, wie es im Modus 13h üblich ist, abgeschaltet werden.
Man beschreibt das TS-Register 4, und zwar setzt man Bit 2 und löscht Bit 3.
Da es natürlich unter den Grafikkartenherstellern wie so oft schwarze Schafe
gibt, deren Karten nicht ganz 100% kompatibel sind (Dankeschööön!), sollte der
Speicherzugriff auf Byte-Adressierung umgeschaltet werden.
Hierzu löscht man im CRTC-Register 14h das Bit 6 und setzt es im CRTC 17h.
procedure SetModeX;assembler;
asm
mov ax,13h { Zuerst den normalen Modus 13h per BIOS aktivieren }
int 10h
mov dx,3C4h
mov al,4 { TS-Register 4 anwählen }
out dx,al
inc dx
in al,dx { Aktuellen Registerinhalt holen }
and al,0F7h { Bit 2 setzen, Bit 3 löschen }
or al,4
out dx,al { Werte zurückschreiben }
dec dx
mov ax,0F02h
out dx,ax { Alle Planes selektieren }
mov ax,0A000h { VGA-Segment nach ES }
mov es,ax
xor di,di
xor ax,ax
mov cx,0FFFFh
cld
rep stosw { Bildschirm löschen }
mov dx,3D4h
mov al,14h { CRTC-Register 14h anwählen }
out dx,al
inc dx
in al,dx { Aktuellen Registerinhalt holen }
and al,0BFh { Bit 6 löschen }
out dx,al
dec dx
mov al,17h { CRTC-Register 17h anwählen }
out dx,al
inc dx
in al,dx
or al,40h { Bit 6 setzen }
out dx,al
end;
Ziemlich langer Code nur für das Setzen eines Videomodus, aber es lohnt sich.
Wie das Setzen erfolgt, könnt ihr den Kommentaren entnehmen.
Nachdem wir also diese Prozedur aufgerufen haben, haben wir also einen schönen
Mode-X Screen vor uns. Toll. Freu. Jubel. Aber was nun?
Natürlich, wie wär's, wenn wir munter und unverfroren ein paar Pixel auf den
Screen knallen würden?
procedure XPutPixel(x,y:integer;col:byte);assembler;
asm
mov ax,0A000h { VGA-Segment nach ES }
mov es,ax
mov cx,x { X-Koord nach CX }
and cx,3 { Plane bestimmen }
mov ax,1
shl ax,cl { Entsprechendes Bit setzen }
mov ah,al
mov dx,03C4h
mov al,2 { Timing-Sequencer Reg. 2 anwählen }
out dx,ax { Plane setzen }
mov ax,80 { Pixel-Offset bestimmen }
mul y { Offset = y * 80 + x div 4 }
mov di,ax { y * 80 nach DI }
mov ax,x
shr ax,2 { entspricht AX div 4 }
add di,ax { x div 4 dazuzählen }
mov al,col { Farbe nach AL }
mov es:[di],al { Pixel setzen }
end;
Die Prozedur selektiert automatisch die richtige Plane und setzt das Pixel.
Logisch, daß die Adressierung des Pixels mehr Zeit braucht als im Modus 13h,
diese Geschwindigkeitsnachteile werden aber durch die oben erwähnten 4 Pages
wieder wettgemacht.
Wollen wir nun auf die zweite Page ein Pixel setzen, müssen wir nur beim
Aufrufen der XPutPixel-Prozedur 200 auf den Y-Wert aufaddieren.
Wenn wir auf eine andere Page umschalten wollen, so daß diese auf dem
Bildschirm sichtbar wird, brauchen wir diese Prozedur:
procedure XSetStart(Adr:word);assembler;
asm
mov dx,3D4h
mov al,0Ch { CRTC-Register 0Ch }
mov ah,byte ptr Adr + 1 { Bits 15-8 abschicken }
out dx,ax
mov al,0Dh { CRTC-Register 0Dh }
mov ah,byte ptr Adr { Bits 7-0 abschicken }
out dx,ax
end;
Diese Prozedur kann übrigens in sämtlichen Videomodi angewendet werden,
allerdings macht es nur Sinn, wenn man mehrere Bildschirmseiten hat, wie z.B.
im Modus 640x480x16 oder auch im Textmodus.
Will man also auf Page 2 umschalten, geben wir als Parameter 16000 an, bei
Page 3 32000 und bei Page 4 48000. Mit dem Parameter 0 gelangen wir wieder
zurück auf die erste Page. Natürlich kann man auch einen Wert wie 8000 angeben,
um die zweite Hälfte der ersten und die erste Hälfte der zweiten Page auf dem
Bildschirm zu haben. Merkt ihr was? Diese Prozedur läßt sich sehr elegant für
ein Scrolling einsetzen, indem man den Wert einfach stufenweise erhöht.
Das hatten wir doch schon im vorletzten Teil! Eine Ergänzung dieser Technik
findet ihr weiter unten.
Am Anfang war die Rede davon, daß man für eine saubere Spritedarstellung oder
ähnliches sorgen kann, indem man das Bild unsichtbar auf der zweiten Page
berechnet und dann auf den Screen kopiert. Der Code dazu:
procedure CopyPage(ziel,quelle:word);assembler;
asm
push ds { DS wird verändert, also sichern }
mov dx,3C4h
mov ax,0F02h
out dx,ax
mov dx,3CEh { Write Mode 1 }
mov ax,4105h
out dx,ax
mov ax,0A000h { VGA-Segment nach DS (Quelle) und ES (Ziel) }
mov ds,ax
mov es,ax
mov si,quelle { Quelloffset nach SI }
mov di,ziel { Zieloffset nach DI }
mov cx,16000 { 16000 Bytes kopieren }
rep movsb
mov dx,3CEh
mov ax,4005h
out dx,ax
pop ds { DS wiederherstellen }
end;
Als Parameter werden wieder die Offsets der Pages übergeben.
Wenn man also das Hintergrundbild auf Page 2 hat, müßte eine Repeat-Schleife
zur Darstellung von Sprites auf einem Hintergrund ungefähr so aussehen:
repeat
WaitRetrace;
CopyPage(0,16000);
DrawTheSprites;
until Bedingung = true;
Dabei muß man sich keine Gedanken darüber machen, daß man den Hintergrund
wiederherstellen muß, da er im nächsten Schleifendurchlauf sowieso neu kopiert
und damit der alte überschrieben wird. Und da nur 16000 Byte kopiert werden
müssen, geht das Ganze auch relativ schnell.
Das war der wichtigste Grund für die Benutzung des Mode-X. Aber er hat noch
andere Vorzüge, die der Modus 13h nicht bietet.
Der Modus 13h hat standardmäßig die vertikale Auflösung von 200 Pixeln. Das
ist wohl jedem bekannt. Das Sonderbare ist nur, daß die VGA eine vertikale
Auflösung von 200 gar nicht unterstützt. Es gibt nur die Möglichkeiten 350, 400
oder 480. Aber wie kommt der Modus 13h dann zustande? Tja, die schlauen
IBM-Leute haben sich das so überlegt: Da 320x400 (=128000) Byte nicht in den
Bildschirmspeicher passen, ließ man sich das sog. Double-Scan-Verfahren
einfallen. Es wird durch das Setzen der Bits 0-3 und 7 des CRTC-Reg. 9 aktiviert.
Es tut nichts anderes, als jede Zeile einfach 2 mal darzustellen. Dadurch
kommen wir auf die Auflösung von 200 Zeilen. Aber was hilft uns das jetzt?
Ganz einfach, wenn das Register gesetzt wird, um die Zeilenanzahl zu halbieren,
brauchen wir die Bits nur wieder zu deaktivieren, um wieder 400 Zeilen zu
erhalten. Denn im Mode-X passen die 320x400 Pixel ohne weiteres 2 mal in den
Bildschirmspeicher, auch wenn wir dann auf 2 Pages verzichten müssen.
procedure XEnter400;assembler;
asm
mov dx,3D4h
mov al,9 { CRTC-Register 9 anwählen }
out dx,al
inc dx
in al,dx { Aktuellen Registerinhalt holen }
and al,70h { Bits 0-3 und 7 löschen }
out dx,al
end;
Durch die verschiedensten Registermanipulationen lassen sich alle möglichen
Auflösungen erreichen, das geht von 256x200 über 376x282 bis 512x480, wobei wir
diese ca. 20 verschiedenen Auflösungen hier nicht besprechen wollen, das würde
nun wirklich zu weit führen. Wer sich für diese VGA-Spielereien interessiert,
soll sich eine ausführliche VGA-Referenz kaufen oder eine der zahlreichen guten
Mode-X Dokumentationen holen, die durch viele Mailboxen geistern.
Kommen wir zu einem weiteren Punkt, wegen dem der Mode-X häufig und gerne
eingesetzt wird: Full-Screen-Scrolling.
Dies ist im Mode-X dank der 4 Pages wirklich kinderleicht zu programmieren.
Es ist ein Scrolling in alle denkbaren Richtungen möglich. Dabei gibt es
allerdings zwei verschiedene Sichtweisen. Aus der Sicht des Programmierers
wird der Screen wie ein Ausschnitt über den Hintergrund bewegt. Aus der Sicht
des Users wird der Hintergrund unter dem Screen bewegt. Dies erscheint
belanglos, es gibt aber einen gravierenden Unterschied zwischen diesen beiden
Sichtweisen, der leicht Verwirrung stiften kann. Ein Beispiel: Ein Scrolly
scrollt zur besseren Lesbarkeit von unten nach oben. Doch der Programmierer
bewegt dabei den Screen von oben nach unten, d.h. die Startadresse des Screens
muß erhöht werden, um das Bild von unten nach oben wandern zu lassen.
Will man allerdings nicht nur nach oben oder nach unten, sondern in alle
Richtungen scrollen, z.B. in einem Actionspiel o.ä., muß der Bildschirm erst
so vorbereitet werden, als ob die vier Pages ein Quadrat bilden würden. Dies
erreichen wir durch das Aktivieren des 160-Byte-Modus. Nach dem Aufrufen der
folgenden Prozedur ist der VGA-Speicher wie ein 640x400-Screen aufgebaut, d.h.
wir können den Ausschnitt des Bildschirms (320x200) beliebig über diesen großen
Bildschirm bewegen. Dazu wird das CRTC-Register 13h mit dem Wert 80 beschrieben
(der normale Wert ist 40), um die virtuelle horizontale Auflösung zu verdoppeln.
Wie dies genau funktioniert, will ich jetzt nicht erklären, das würde zu weit
führen. Wichtig ist nur, daß es funktioniert.
procedure XDouble;assembler;
asm
mov dx,3D4h { CRTC-Register 13h anwählen }
mov ax,5013h { auf 80 setzen (doppelte Breite) }
out dx,ax
end;
Um das Prinzip des Scrollings in verschiedene Richtungen zu verdeutlichen,
folgt nun zum Schluß noch einmal ein Beispiel-Programm, das den kompletten
virtuellen 640x400-Screen mit Pixeln vollschreibt und anschließend scrollt.
uses crt;
var x,y,dirx,diry : word;
{ Hier die Prozeduren SetModeX, WaitRetrace, XSetStart, XDouble und
XPutPixel einfügen }
begin
setmodex; { Mode-X setzen }
xdouble; { 160-Byte Modus einschalten }
for x := 0 to 319 do { Alle 4 Pages mit Müll füllen }
for y := 0 to 799 do xputpixel(x,y,random(256));
x := 1; { Startposition }
y := 160;
dirx := 1;
diry := 160;
repeat
inc(x,dirx); { Bildausschnitt weiterbewegen }
inc(y,diry);
delay(10);
WaitRetrace;
XSetStart(y+x); { Neue Startadresse setzen }
if (x = 80) or (x = 1) then dirx := -dirx;
if (y = 32000) or (y = 160) then diry := -diry;
{ Richtung umkehren, wenn am Rand angekommen }
until keypressed;
readkey;
asm mov ax,3; int 10 end;
end.
Wer einen Loader für 640x400-Bilder hat, kann ja statt dem Pixelmüll mal ein
schönes Hintergrundbild laden.
Weitere empfehlenswerte Mode-X Kurse:
'Assembler-Kurs - Der Mode X' von Midnight/Knockout, Deutsch (in Blackmail #10)
'Introduction to Mode X' von Robert Schmidt, Englisch (in PCGPE 1.0)
Außerdem die Mode-X-Libraries von Draeden/VLA und Matt Pritchard.
OK, Freunde, das war's für diesmal, für mich war es der am schwersten zu
erklärende Teil bis jetzt, ich hoffe, ihr habt alles kapiert, wenn nicht, lest
es euch noch mal durch, es ist wirklich nicht leicht. Wenn ihr es dann immer
noch nicht versteht, liegt es wohl an meinen Formulierungen. In dem Fall könnt
ihr euch aber noch eine andere Referenz zu dem Thema aus den obengenannten
Quellen ziehen.
Im nächsten Teil geht's um ein Thema, über das ich schon viele Fragen aber
wenig Antworten gelesen habe: Wie stelle ich eigentlich ein Bild dar, das ich
gezeichnet habe?
Wir werden Loader für PCX und RAW-Grafiken besprechen und vielleicht werde ich
noch einiges über Bilddatenkompression schreiben.
Das wird zwar für die VGA-Freaks da draußen ein ziemlich langweiliger Artikel,
aber ich bin sicher, daß sich einige Anfänger, die sich die Zähne an diesem
Problem ausgebissen haben, gern einen Artikel darüber hätten. Falls ich da
völlig falsch liege, könnt ihr's mir ja sagen. Bis dann!
[ This text copyright (c) 1995-96 Johannes Spohr. All rights reserved. ]
[ Distributed exclusively through PC-Heimwerker, Verlag Thomas Eberle. ]
[ ]
[ No part of this document may be reproduced, transmitted, ]
[ transcribed, stored in a retrieval system, or translated into any ]
[ human or computer language, in any form or by any means; electronic, ]
[ mechanical, magnetic, optical, chemical, manual or otherwise, ]
[ without the expressed written permission of the author. ]
[ ]
[ The information contained in this text is believed to be correct. ]
[ The text is subject to change without notice and does not represent ]
[ a commitment on the part of the author. ]
[ The author does not make a warranty of any kind with regard to this ]
[ material, including, but not limited to, the implied warranties of ]
[ merchantability and fitness for a particular purpose. The author ]
[ shall not be liable for errors contained herein or for incidental or ]
[ consequential damages in connection with the furnishing, performance ]
[ or use of this material. ]